package com.hyw.trace.configuration.sampler;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.hyw.trace.configuration.ZTraceClientAutoConfiguration.SamplerConfiguration;
import com.hyw.trace.configuration.meta.IEventStorage;
import com.hyw.trace.configuration.meta.TraceEvent;
import com.hyw.trace.configuration.meta.TraceSampler;
import com.hyw.trace.configuration.sample.annotation.TraceSource;
import com.hyw.trace.utils.InstanceFactory;

/**
 * 构造一个针对采集的处理模板类,同时,相关字段以保护方式抽离 , 以便后续业务 变更时, 可以通过继承的方式做行为变动
 * 
 * @author hongyu
 */
public class TraceSamplerTemplate extends AbstractSamplerTemplate {
  private static final Logger log = LoggerFactory.getLogger(TraceSamplerTemplate.class);

  protected Map<TraceSource, Boolean> supportCaches = new ConcurrentHashMap<>();

  protected ExecutorService executor;

  protected SamplerConfiguration samplerConfiguration;

  protected IEventStorage<TraceEvent> outcomeStorage;

  public TraceSamplerTemplate(IEventStorage<TraceEvent> outcomeStorage, SamplerConfiguration samplerConfiguration) {
    super();
    Objects.requireNonNull(outcomeStorage);
    Objects.requireNonNull(samplerConfiguration);
    this.outcomeStorage = outcomeStorage;
    this.samplerConfiguration = samplerConfiguration;
    this.executor = new ThreadPoolExecutor(samplerConfiguration.getDefaultCoreThread(),
        samplerConfiguration.getDefaultMaxThread(), samplerConfiguration.getDefaultThreadKeepalive(),
        TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(samplerConfiguration.getDefaultMaxQueue()),
        r -> new Thread(r, "ztracer-sampler-executor-" + r.hashCode()));
  }

  @Override
  protected boolean isSupport(TraceSource annotation) {
    boolean valid = super.isSupport(annotation);
    if (valid) {// 采样速率的控制
      int r = annotation.ratio();
      if (r == MIN_BOUND) {// 如果为0时，则完全不采集
        valid = false;
      } else {// 如果随机得到的值小于等于预设比例值，则进行计算
        int seed = ThreadLocalRandom.current().nextInt(1, MAX_BOUND);
        valid = (seed <= r);
      }
    }

    return valid && supportCaches.computeIfAbsent(annotation, key -> {
      boolean flag = true;
      if (StringUtils.hasText(samplerConfiguration.getGroupSkipPattern())) {
        flag = !Pattern.matches(samplerConfiguration.getGroupSkipPattern(), key.group());
      }

      if (StringUtils.hasText(samplerConfiguration.getTypeSkipPattern())) {
        flag = !Pattern.matches(samplerConfiguration.getTypeSkipPattern(), key.type());
      }
      return flag;
    });
  }

  @Override
  public void run(Object[] args, TraceSource annotation) {
    this.run(args, annotation, p -> new TraceEvent(annotation.group(), annotation.type(), annotation.description(), p));
  }

  @Override
  @SuppressWarnings({ "rawtypes", "unchecked" })
  public void run(Object[] args, TraceSource annotation, Function<Map<String, ?>, TraceEvent> mapper) {
    if (isSupport(annotation)) {
      Entry<Class<?>, Object> paramEntry = extract(args, annotation.sampler());
      TraceSampler sampler = InstanceFactory.getInstance(annotation.sampler());

      // 执行主体
      Runnable task = () -> {
        TraceEvent event = mapper.apply(sampler.process(paramEntry.getValue()));
        if (!CollectionUtils.isEmpty(event.getProperties())) {
          log.debug("Sampling:{}", event);
          outcomeStorage.storage(event);
        } else {
          log.warn("group[{}]->type[{}]->description[{}] 采集到的数据基础存在为空", event.getGroup(), event.getType(),
              event.getDescription());
        }
      };

      if (annotation.async()) {
        CompletableFuture.runAsync(task, executor).exceptionally(e -> {
          log.error("", e);
          return null;
        });
      } else {
        try {
          task.run();
        } catch (Exception ex) {
          log.error("", ex);
        }
      }
    }
  }

  /**
   * 根据注解内容, 从方法签名中提取对应的参数
   * 
   * @return 其中Key为变量的类型, value为对应的值
   */
  @SuppressWarnings({ "rawtypes" })
  private Entry<Class<?>, Object> extract(Object[] args, Class<? extends TraceSampler> subClz) {
    if (null == args || args.length == 0) {
      throw new IllegalArgumentException("参数列表不允许为空");
    }

    Object value = null;

    final Class clz = InstanceFactory.getGenericType(subClz, TraceSampler.class);
    if (Objects.equals(clz, Object.class)) {
      value = args[0];
    } else if (Objects.equals(clz, Object[].class)) {
      value = args;
    } else {
      Optional<Object> targets = Arrays.asList(args).stream().filter(clz::isInstance).findAny();
      if (!targets.isPresent()) {
        throw new IllegalArgumentException("找不到期望值入参");
      }
      value = targets.get();
    }

    Map<Class<?>, Object> data = new HashMap<>(1);
    data.put(clz, value);
    return data.entrySet().iterator().next();
  }

}
